import random
cats = 10

class Player:
    def __init__(self):
        """
        Optional __init__ method is run once when your Player object is created before the
        game starts

        You can add other internal (instance) variables here at your discretion.

        You don't need to define food or reputation as instance variables, since the host
        will never use them. The host will keep track of your food and reputation for you
        as well, and return it through hunt_choices.
        """
        self.reputation = 0.0
        self.repuwin = [[] for i in range(cats)] # Stores how good is a reputation
        self.mwin = [[] for i in range(cats)] # Stores how likely is it to win for a given value of m.

    # All the other functions are the same as with the non object oriented setting (but they
    # should be instance methods so don't forget to add 'self' as an extra first argument).

    def hunt_choices(self, round_number, current_food, current_reputation, m, player_reputations):
        self.P = len(player_reputations) + 1
        self.m = m
        mph = float(m)/self.P/(self.P-1)
         self.reputation = current_reputation
        if current_food < 10*(self.P-1): # I am going to starve. Slack as much as possible.
            self.response = ['s' for x in player_reputations]
            return self.response
        else: # We keep track of how to behave with a 
            if round_number <= 5: # Start by building reputation
                probs = [1.0 - 0.1 * round_number for x in player_reputations]
            elif round_number <= 20 or random.random() < 0.05: # In the first few rounds play independently. Do this sometimes randomly.
                probs = [mph + 0.01 for x in player_reputations]
            else:
                probs = [i if i < 0.9 else 1.1 - i for i in player_reputations] # Don't pay much attention to high rep people.
                # Now we decide whether to try for m.
                mcat = int(cats*mph*0.99)
                # if too less data, try. Otherwise try only if feasible
                if len(self.mwin[mcat]) < 4 or 0.9 < late_average(self.mwin[mcat]) < 1.2:
                    if sum(probs)*self.P < m:
                        incr = (float(m)/self.P - sum(probs))/(self.P - 1)
                        probs = [i+incr for i in probs] # move up probabilities
                else:
                    # if low on food or above optimal reputation, save some food

                    # calculating optimal reputation.
                    repuavs = [late_average(i) if len(i) > 2 else 0.1 for i in self.repuwin]
                    repunorm = [repuavs[i]/(float(i)/cats + 0.12) for i in range(cats)]
                    opt_repu = (repunorm.index(max(repunorm))+1.0)/cats
                    # put manual bar on opt_repu
                    opt_repu = min(0.85, opt_repu)

                    if current_food < 100*(self.P-1) or self.reputation > opt_repu:
                        incr = -0.2
                        probs = [i+incr for i in probs] # add 20% chance of slacking
                    else:
                        incr = opt_repu - self.reputation
                        probs = [i+incr for i in probs] # Try to attain optimal
            #print (*["{0:3.2f}".format(i) for i in probs]) #Debug String
            self.response = ['s' if random.random() > i else 'h' for i in probs]
            return self.response

    # The following two functions try to update the win lists.
    def hunt_outcomes(self, food_earnings):
        repucat = int(cats*self.reputation*0.99)
        self.repuwin[repuwin].append(len([1 for i in food_earnings if i>=0])/float(self.P))

    def round_end(self, award, m, number_hunters):
        mph = float(m)/self.P/(self.P-1)
        mcat = int(cats*mph*0.99)
        t = float(number_hunters/m) # Crucial assumption: m > 0
        t = min(max(t, 0.5), 2.0) # Bound it. Extreme cases can't have too much effect.
        self.mwin[mcat].append(t)

def late_average(L):
    """ Averages with higher weight for later entries"""
    S = 0.0
    if len(L) < 1: return S # Avoid zero division error
    for i in range(len(L)):
        S += (i+1)*L[i]
    return 2.0*S/len(L)/(len(L)+1)

# This player tries to stay alive by maintaining a good(optimal) reputation. We try
# to calculate this optimal by returns for investment basis. It also uses
# a lot of randomization. In the extreme case of starving, we try to slack
# everytime.
